/**
 * 
 */
package contegor.scire.structure;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import contegor.scire.core.Id;
import contegor.scire.core.Link;
import contegor.scire.core.MissingElementException;
import contegor.scire.core.ReturnPoint;
import contegor.scire.core.Task;
import contegor.scire.core.TaskId;
import contegor.scire.core.TransactionId;
import contegor.scire.core.WrongStateException;
import contegor.scire.core.queries.Query;

/**
 * {@link DockingModule} implementation that connects a predefined number of {@link ConceptCloud}s
 * inside a process.
 * This implementation does not duplicate the {@link ConceptCloud}s and {@link Link}s
 * result of any {@link Query}, so it can not be used if those results are going to be modified.
 * 
 * @version $Revision: 1.3 $
 * @author  Héctor Cirbián Pousa ($Author: hector $)
 */
public class IntraProcessDockingModule implements DockingModule {

  private int                     maxClouds;
  private ArrayList<ConceptCloud> clouds;
  private ReturnPoint<Void>       rp;

  /**
   * Constructs a new {@link IntraProcessDockingModule}.
   * 
   * @param maxGates int max number of gates for this {@link DockingModule}.
   * @param rp {@link ReturnPoint} that will receive the answers and {@link Exception}s from
   *        {@link Task}s result of docking two {@link ConceptCloud}s.
   */
  public IntraProcessDockingModule(int maxClouds, ReturnPoint<Void> rp) {
    this.maxClouds = maxClouds;
    this.clouds    = new ArrayList<ConceptCloud>(maxClouds);
    this.rp        = rp;
  }

  @Override
  public <T> void submit(Task<T> task, ReturnPoint<? super T> rp) {
    for (ConceptCloud cloud: clouds) {
      if (task.getTargetContainerId().sameRoot(cloud.getRootId())) {
        cloud.process(task, rp);
        return;
      }
    }
    rp.manage(task.getTaskId(), new MissingElementException("No ConceptCloud found for target: " + task.getTargetContainerId()));
    rp.finish(task.getTaskId());
  }

  @Override
  public void commit(TaskId taskId) throws Exception {
    for (ConceptCloud cloud: clouds)
      cloud.commit(taskId);
  }

  @Override
  public void rollback(TaskId taskId) throws Exception {
    for (ConceptCloud cloud: clouds)
      cloud.rollback(taskId);
  }

  @Override
  public void open(ConceptCloud cloud) throws WrongStateException {
    if (clouds.size() >= maxClouds)
      throw new WrongStateException("Exceded number of connections.");
    for (ConceptCloud other: clouds) {
      List<Task<Void>> tasks;
      // Updates from cloud to other.
      tasks = cloud.getLinkTrackingTasksFor(other.getRootId());
      for (Task<Void> task: tasks)
        other.process(task, this.rp);
      // Updates from other to cloud.
      tasks = other.getLinkTrackingTasksFor(cloud.getRootId());
      for (Task<Void> task: tasks)
        cloud.process(task, this.rp);
    }
    clouds.add(cloud);
  }

  @Override
  public void close(ConceptCloud cloud) throws WrongStateException {
    if (!clouds.remove(cloud))
      throw new WrongStateException("Docking does not exist.");
    for (ConceptCloud other: clouds) {
      cloud.stopTrackingLinksOf(other.getRootId());
      other.stopTrackingLinksOf(cloud.getRootId());
    } 
  }

  @Override
  public Map<Id, Set<TransactionId>> getPendingTransactions() {
    Map<Id, Set<TransactionId>> result = new TreeMap<Id, Set<TransactionId>>();
    for (ConceptCloud cloud: this.clouds)
      result.putAll(cloud.getPendingTransactions());
    return result;
  }
}